> Claude Agent Patterns
Budding
planted Jan 8, 2026tended Jan 8, 2026
#ai-agents#claude#anthropic#patterns#best-practices
Claude Agent Patterns
πΏ Budding note β Claude-specific agent development.
Why Claude for Agents?
Claude excels at agent tasks due to:
- Native tool use: Built-in function calling
- Long context: 200K tokens for extensive memory
- Reasoning: Strong chain-of-thought capabilities
- Safety: Reduced prompt injection vulnerability
- Extended thinking: Deep reasoning with
thinkingblocks
Related: AI Agents Fundamentals for core concepts
Basic Agent Loop
from anthropic import Anthropic
client = Anthropic()
def claude_agent(task: str, tools: list, max_iterations: int = 10) -> str:
"""Simple Claude agent with tool use"""
messages = [{"role": "user", "content": task}]
for iteration in range(max_iterations):
response = client.messages.create(
model="claude-sonnet-4-5-20250929",
max_tokens=4096,
tools=tools,
messages=messages
)
# Add assistant response
messages.append({
"role": "assistant",
"content": response.content
})
# Check if done
if response.stop_reason == "end_turn":
return extract_text(response)
# Execute tools
elif response.stop_reason == "tool_use":
tool_results = []
for block in response.content:
if block.type == "tool_use":
result = execute_tool(block.name, block.input)
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": str(result)
})
messages.append({
"role": "user",
"content": tool_results
})
raise Exception("Max iterations reached")
def extract_text(response) -> str:
"""Get text from response"""
for block in response.content:
if hasattr(block, "text"):
return block.text
return ""
def execute_tool(name: str, args: dict) -> any:
"""Execute tool by name"""
tools_map = {
"web_search": web_search,
"calculator": calculator,
# Add your tools here
}
return tools_map[name](**args)
Extended Thinking for Complex Tasks
Use thinking blocks for deep reasoning:
response = client.messages.create(
model="claude-sonnet-4-5-20250929",
max_tokens=16000,
thinking={
"type": "enabled",
"budget_tokens": 10000
},
messages=[{
"role": "user",
"content": "Plan a complex software refactoring"
}]
)
# Access thinking process
for block in response.content:
if block.type == "thinking":
print(f"Reasoning: {block.thinking}")
elif block.type == "text":
print(f"Response: {block.text}")
When to use extended thinking:
- Complex reasoning tasks
- Planning and strategizing
- Code analysis and debugging
- Mathematical proofs
Prompt Caching for Efficiency
Cache system prompts and tools:
response = client.messages.create(
model="claude-sonnet-4-5-20250929",
max_tokens=1024,
system=[
{
"type": "text",
"text": "You are an expert coding assistant...",
"cache_control": {"type": "ephemeral"}
}
],
tools=tools, # Tools are automatically cached
messages=messages
)
# Cached content reused across requests
# Significant cost savings for repeated agent loops
Benefits:
- Reduce latency (cached content processes faster)
- Lower costs (5 minute cache window)
- Ideal for agent loops with stable system prompts
Tool Use Patterns
Structured Tool Output
tools = [
{
"name": "analyze_code",
"description": "Analyze code for issues",
"input_schema": {
"type": "object",
"properties": {
"code": {"type": "string"},
"language": {"type": "string"}
},
"required": ["code", "language"]
}
}
]
# Claude returns structured calls
# {
# "name": "analyze_code",
# "input": {
# "code": "def foo():\n pass",
# "language": "python"
# }
# }
Tool Chaining
Claude naturally chains tools:
# Task: "Find weather in user's location and convert to Fahrenheit"
# Claude will:
# 1. Call get_user_location()
# 2. Call get_weather(location)
# 3. Call convert_temperature(celsius, "fahrenheit")
# 4. Respond with result
Error Handling
def execute_tool_safely(name: str, args: dict) -> dict:
"""Execute with error context"""
try:
result = tools[name](**args)
return {
"success": True,
"result": result
}
except Exception as e:
return {
"success": False,
"error": str(e),
"suggestion": f"Try checking the {name} parameters"
}
# Return error details to Claude
tool_result = {
"type": "tool_result",
"tool_use_id": tool_use_id,
"content": json.dumps(execute_tool_safely(name, args)),
"is_error": not result["success"]
}
Related: Tool Use and Function Calling
Memory Management
Conversation Summarization
async def summarize_if_needed(messages: list, client) -> list:
"""Compress old messages when context gets full"""
if estimate_tokens(messages) > 150000: # Leave room for response
# Summarize first half
to_summarize = messages[:len(messages)//2]
summary = await client.messages.create(
model="claude-sonnet-4-5-20250929",
max_tokens=2000,
messages=[{
"role": "user",
"content": f"Summarize this conversation:\n{to_summarize}"
}]
)
# Keep summary + recent messages
return [
{"role": "user", "content": f"[Previous conversation summary: {summary.content[0].text}]"},
*messages[len(messages)//2:]
]
return messages
Long-Term Memory with RAG
from qdrant_client import QdrantClient
class ClaudeAgentWithMemory:
def __init__(self):
self.client = Anthropic()
self.vector_store = QdrantClient(":memory:")
async def process_with_memory(self, query: str):
# Recall relevant memories
relevant = self.vector_store.search(query, limit=5)
# Build context
memory_context = "\n".join([
f"- {mem.payload['text']}" for mem in relevant
])
# Query with memory
response = await self.client.messages.create(
model="claude-sonnet-4-5-20250929",
max_tokens=4096,
system=f"Relevant past context:\n{memory_context}",
messages=[{"role": "user", "content": query}]
)
# Store new memory
await self.store_memory(f"Q: {query}\nA: {response.content[0].text}")
return response
Related: Agent Memory Systems
Self-Reflection Pattern
Claude critiques its own work:
async def self_reflecting_agent(task: str):
"""Agent reflects on and improves its work"""
# Initial attempt
response = await client.messages.create(
model="claude-sonnet-4-5-20250929",
max_tokens=4096,
messages=[{"role": "user", "content": task}]
)
initial_work = response.content[0].text
# Self-critique
critique = await client.messages.create(
model="claude-sonnet-4-5-20250929",
max_tokens=4096,
messages=[{
"role": "user",
"content": f"""Review this work and provide critique:
Task: {task}
Work: {initial_work}
What could be improved? Be specific and constructive."""
}]
)
critique_text = critique.content[0].text
# Revise based on critique
revised = await client.messages.create(
model="claude-sonnet-4-5-20250929",
max_tokens=4096,
messages=[{
"role": "user",
"content": f"""Improve this work based on the critique:
Original: {initial_work}
Critique: {critique_text}
Provide the improved version."""
}]
)
return revised.content[0].text
Multi-Turn Workflows
Complex tasks need planning:
async def planning_agent(goal: str):
"""Agent plans then executes"""
# Planning phase
plan = await client.messages.create(
model="claude-sonnet-4-5-20250929",
max_tokens=4096,
messages=[{
"role": "user",
"content": f"""Break down this goal into steps:
Goal: {goal}
Create a detailed step-by-step plan."""
}]
)
steps = parse_steps(plan.content[0].text)
# Execution phase
results = []
for step in steps:
result = await execute_step(step)
results.append(result)
# Synthesis
final = await client.messages.create(
model="claude-sonnet-4-5-20250929",
max_tokens=4096,
messages=[{
"role": "user",
"content": f"""Synthesize these results:
Goal: {goal}
Results: {results}"""
}]
)
return final.content[0].text
Safety Patterns
Sandboxed Tool Execution
def safe_code_execution(code: str) -> str:
"""Execute with restrictions"""
# Use restricted Python environment
import RestrictedPython
compiled = RestrictedPython.compile_restricted(
code,
filename='<agent_code>',
mode='exec'
)
safe_globals = {
'__builtins__': safe_builtins,
'print': safe_print,
# Limited standard library
}
exec(compiled, safe_globals)
Human-in-the-Loop
async def human_approval_agent(task: str):
"""Require approval for sensitive actions"""
response = await client.messages.create(
model="claude-sonnet-4-5-20250929",
max_tokens=4096,
tools=tools,
messages=[{"role": "user", "content": task}]
)
# Check for sensitive tools
for block in response.content:
if block.type == "tool_use" and is_sensitive(block.name):
print(f"Agent wants to: {block.name}({block.input})")
approval = input("Approve? (yes/no): ")
if approval.lower() != "yes":
return "Action rejected by human"
# Proceed if approved
return await execute_with_approval(response)
Related: Agent Security Considerations
Performance Optimization
Batched API Calls
Use message batches API:
# Create batch of requests
requests = [
{
"custom_id": f"request-{i}",
"params": {
"model": "claude-sonnet-4-5-20250929",
"max_tokens": 1024,
"messages": [{"role": "user", "content": task}]
}
}
for i, task in enumerate(tasks)
]
# Submit batch
batch = client.batches.create(requests=requests)
# Check status later
while batch.processing_status != "ended":
await asyncio.sleep(10)
batch = client.batches.retrieve(batch.id)
# Get results
for result in batch.results:
print(result.result.message.content[0].text)
Streaming Responses
def streaming_agent(task: str):
"""Stream responses for better UX"""
with client.messages.stream(
model="claude-sonnet-4-5-20250929",
max_tokens=4096,
messages=[{"role": "user", "content": task}]
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
Connection Points
Prerequisites:
- AI Agents Fundamentals β Agent basics
- Tool Use and Function Calling β Tool patterns
Related:
- Agent Frameworks Comparison β Compare with LangChain
- Agent Memory Systems β Memory for Claude agents
- Agent Security Considerations β Claude safety
Advanced:
- Production Agent Deployment β Scaling Claude agents
- Building Agents with LangChain β LangChain + Claude
>> referenced by (8)
Agent Frameworks Comparison
...g Claude-specific features - Simple agent that doesn't need framework Related: [[Claude Agent Patterns]] LlamaIndex Best for: RAG + agents, document-heavy workflows Overv...
Agent Memory Systems
...t basics - [[Tool Use and Function Calling]] β Memory as a tool Related: - [[Claude Agent Patterns]] β Claude-specific memory handling - [[Agent Security Considerations]] β Memory...
Agent Security Considerations
...ag in response.lower(): return False return True `` Related: [[Claude Agent Patterns]] for Claude's built-in protections Tool Security Least Privilege ``py...
AI Agents
...ecific - [[Building Agents with LangChain]] πΏ β LangChain development guide - [[Claude Agent Patterns]] πΏ β Best practices for Claude - [[Agent Frameworks Comparison]] πΏ β LangChain...
AI Agents Fundamentals
...Open-source alternative - Gemini Pro: Google's multimodal option Related: [[Claude Agent Patterns]] for Claude-specific best practices 2. Memory Systems Agents need memory t...
Building Agents with LangChain
...basics - [[Agent Frameworks Comparison]] β Framework comparison Related: - [[Claude Agent Patterns]] β Using Claude with LangChain - [[Tool Use and Function Calling]] β Tool patter...
Production Agent Deployment
...uction Related: - [[Agent Frameworks Comparison]] β Framework deployment - [[Claude Agent Patterns]] β Claude optimization - [[Agent Memory Systems]] β Production memory **Testing...
Tool Use and Function Calling
...tools=tools, messages=messages ) ``` Related: [[Claude Agent Patterns]] for Claude-specific patterns Tool Definition Best Practices 1. Clear D...